#pragma once

#include "limbo/ethernet/mac_address.h"
#include "limbo/ip/ipv4_address.h"
#include <cstdint>

namespace limbo::arp {

template <std::uint32_t N, typename Time = uint32_t> struct ArpTable {

  struct Item {
    ethernet::MacAddress mac;
    ip::IPv4Address ip;
    Time expires_at;
  };

  ArpTable() {
    for (std::uint32_t i = 0; i < N; ++i) {
      items[i] = Item{ethernet::MacAddress::unknown,
                      ip::IPv4Address::from_network(0), 0};
    }
  }

  void update(const ethernet::MacAddress &mac, const ip::IPv4Address &ip,
              Time expires_at) {
    std::uint32_t j = 0;
    Time oldest = items[0].expires_at;
    for (std::uint32_t i = 1; i < N; ++i) {
      auto item = items[i];
      if (item.expires_at < oldest) {
        j = i;
        oldest = item.expires_at;
      }
      if (item.mac == mac) {
        j = i;
        break;
      }
    }
    items[j] = {mac, ip, expires_at};
  }

  const ip::IPv4Address *get(const ethernet::MacAddress &mac, Time now) {
    for (std::uint32_t i = 0; i < N; ++i) {
      auto &item = items[i];
      if ((item.expires_at > now) && (item.mac == mac)) {
        return &item.ip;
      }
    }
    return nullptr;
  }

  const ethernet::MacAddress *get(const ip::IPv4Address &ip, Time now) {
    for (std::uint32_t i = 0; i < N; ++i) {
      auto &item = items[i];
      if ((item.expires_at > now) && (item.ip == ip)) {
        return &item.mac;
      }
    }
    return nullptr;
  }

  Item items[N];
};

} // namespace limbo::arp
